// MIT License
// 开源地址：https://gitee.com/co1024/AbcMvc
// Copyright (c) 2021-2022 1024
// Abc.Mvc=Furion+EF+SqlSugar+Pear layui admin.

using Microsoft.Extensions.Logging;

namespace Abc.Base;

/// <summary>
/// Cookie 帮助类
/// </summary>
public static class AppEx
{
    #region Furion Log

    /// <summary>
    /// Microsoft.Extensions.Logging.ILogger 静态写日志快捷方式，与Furion提供Log.xxx 有区别，做了一些个性化设置
    /// </summary>
    public static Microsoft.Extensions.Logging.ILogger Log => GetLogger();

    /// <summary>
    /// 日志对象集合
    /// </summary>
    private static readonly ConcurrentDictionary<string, LogCacheItem<Microsoft.Extensions.Logging.ILogger>> Loggers = new();

    /// <summary>
    ///
    /// </summary>
    /// <param name="floderName">文件夹名称，例如：abc/file</param>
    /// <param name="categoryName">分类</param>
    /// <param name="maxRollingFiles"> 如果指定了该值，那么超出该值将从最初日志文件中从头写入覆盖，默认按一天24个文件，一个小时一个，保存30天</param>
    /// <param name="fileSizeLimitBytes"> 如果指定了该值，那么日志文件大小超出了该配置就会创建的日志文件，新创建的日志文件命名规则：文件名+[递增序号].log，默认100M一个文件</param>
    /// <param name="useUtcTimestamp">是否使用 UTC 时间戳，默认 false</param>
    /// <param name="useUtcTimestamp">最低日志记录级别，默认 Trace</param>
    /// <returns></returns>
    public static Microsoft.Extensions.Logging.ILogger GetLogger(string floderName = "all"
        , string categoryName = "default"
        , int maxRollingFiles = 24 * 30
        , long fileSizeLimitBytes = 100 * 1024 * 1024
        , bool useUtcTimestamp = false
        , LogLevel minimumLevel = LogLevel.Trace)
    {
        //判断缓存的 ILogger 是否已存在，如果改以存在从字典集合直接取出使用，不在重新创建
        LogCacheItem<Microsoft.Extensions.Logging.ILogger> logCacheItem;
        var state = Loggers.TryGetValue(floderName, out logCacheItem);
        var IsGetLogger = false;//是否从缓存中取出Logger对象
        if (state == true && logCacheItem != null)
        {
            if (logCacheItem.Date == $"{DateTimeOffset.Now:yyyy-MM-dd}")
            {
                IsGetLogger = true;
            }
            else
            {
                //移除旧的Logger对象
                var removestate = Loggers.TryRemove(floderName, out var oldlogger);
            }
        }
        if (IsGetLogger == false)
        {

            var loggerFactory = App.GetRequiredService<ILoggerFactory>(App.RootServices);
            //Array.ForEach(new[] { LogLevel.Information, LogLevel.Warning, LogLevel.Error }, logLevel =>
            //Array.ForEach(Enum.GetValues(typeof(LogLevel)), logLevel =>
            foreach (LogLevel logLevel in Enum.GetValues(typeof(LogLevel)))
            {
                loggerFactory.AddFile("logs/{2}/{1}-{0:yyyy}-{0:MM}-{0:dd}-{0:HH}.log", options =>
                {
                    options.FileNameRule = fileName => string.Format(fileName, DateTime.UtcNow, logLevel.ToString(), floderName.Replace("\\", "/").Trim('/'));
                    options.WriteFilter = logMsg => logMsg.LogLevel == logLevel;
                    options.MaxRollingFiles = maxRollingFiles;
                    options.FileSizeLimitBytes = fileSizeLimitBytes;
                    options.UseUtcTimestamp = useUtcTimestamp;//是否使用 UTC 时间戳，默认 false
                    options.MinimumLevel = minimumLevel;
                });
            };

            logCacheItem = new LogCacheItem<ILogger>();
            logCacheItem.Logger = loggerFactory.CreateLogger(categoryName);

            Loggers.TryAdd(floderName, logCacheItem);
        }
        else
        {
            // return serilogCacheItem.Logger;
            if (logCacheItem != null)
            {
                return logCacheItem.Logger;
            }
        }
        return Furion.Logging.Log.CreateLogger<Microsoft.Extensions.Logging.ILogger>(); ;

    }

    #endregion Furion Log



    #region 访问计数，访问防重复执行


    #region 旧方法【超过限制次数，可继续执行】

    /// <summary>
    /// 获取访问次数
    /// 每次调用方法，对应key的值都会加 1，创建的新值默认为1
    /// </summary>
    /// <param name="key">关键词</param>
    /// <param name="maxcount">最大计数，超过此计数会清0</param>
    /// <returns></returns>
    public static int AllowMultithreadingExec(string key, int maxcount = -1)
    {
        var value = ParallelExecutions.AddOrUpdate(key, 1, (key, oldvalue) => oldvalue + 1);

        if (maxcount > 0 && value > maxcount)
        {
            value = ParallelExecutions.AddOrUpdate(key, 1, (key, oldvalue) => 0);
            return value;
        }
        else
        {
            return value;
        }
    }

    /// <summary>
    /// 清理访问次数，清理后的访问次数为0
    /// </summary>
    /// <param name="key"></param>
    public static void AllowMultithreadingExecClear(string key)
    {
        var value = ParallelExecutions.AddOrUpdate(key, 0, (key, oldvalue) => 0);
    }

    #endregion 旧方法
    /// <summary>
    /// 访问计数，防止锁死
    /// </summary>
    private static readonly ConcurrentDictionary<string, int> ParallelExecutions = new();



    #region 新方式【不能超过并行执行的最大值】
    /// <summary>
    /// 新增并行任务
    /// </summary>
    /// <param name="key">关键词</param>
    /// <param name="maxParallelCount">最大并行数，默认为0，不限制</param>
    /// <returns><是否添加并行成功,并行数></returns>
    public static Tuple<bool, int> PeAdd(string key, int maxParallelCount = 0)
    {
        var value = ParallelExecutions.AddOrUpdate(key, 1, (key, oldvalue) => oldvalue + 1);
        if (maxParallelCount > 0 && value > maxParallelCount)
        {
            value = ParallelExecutions.AddOrUpdate(key, 1, (key, oldvalue) => (oldvalue > 0 ? oldvalue - 1 : 0));
        }
        return Tuple.Create(true, value);
    }

    /// <summary>
    /// 清理并行任务
    /// </summary>
    /// <param name="key">关键词</param>
    public static void PeClear(string key)
    {
        var value = ParallelExecutions.AddOrUpdate(key, 0, (key, oldvalue) => 0);
    }

    /// <summary>
    /// 从集合移除并行任务
    /// </summary>
    /// <param name="key">关键词</param>
    /// <returns></returns>
    public static Tuple<bool, int> PeRemove(string key)
    {
        var state = ParallelExecutions.TryRemove(key, out var value);
        return Tuple.Create(state, value);
    }

    /// <summary>
    /// 并行任务完成,并行数减1
    /// </summary>
    /// <param name="key">关键词</param>
    /// <returns>剩余并行数</returns>
    public static int PeEnd(string key)
    {
        //新增或减1
        var value = ParallelExecutions.AddOrUpdate(key, 0, (key, oldvalue) => (oldvalue > 0 ? oldvalue - 1 : 0));
        return value;
    }

    /// <summary>
    /// 获取PE的值
    /// </summary>
    /// <param name="key">关键词</param>
    /// <returns></returns>
    public static int PeGetValue(string key)
    {
        var state = ParallelExecutions.TryGetValue(key, out var value);
        return state ? value : 0;
    }
    #endregion


    #endregion 访问计数，访问防重复执行


    /// <summary>
    /// 获取上传文件的文件夹
    /// </summary>
    /// <returns></returns>
    public static string GetUploadDefaultFloder()
    {
        //默认物理文件夹
        var defaultPhysicalPath = App.Configuration["AppInfo:WebUploadFloder"];
        if (string.IsNullOrWhiteSpace(defaultPhysicalPath) || !Directory.Exists(defaultPhysicalPath))
        {
            //如果不存在，设置默认的物理文件，网站更目录下
            if (App.WebHostEnvironment != null)
            {
                defaultPhysicalPath = App.WebHostEnvironment.ContentRootPath;
            }
            else if (App.HostEnvironment != null)
            {
                defaultPhysicalPath = App.HostEnvironment.ContentRootPath;
            }
            else
            {
                throw Oops.Bah("WebHostEnvironment 和 HostEnvironment 都为空了");
            }
        }

        return defaultPhysicalPath;
    }

    /// <summary>
    /// 获取上传文件完整路径
    /// </summary>
    /// <param name="fileRelativePath">文件相对路径（例如：/u/f/20220101/xxxx.png）</param>
    /// <param name="logicalPath">URL访问的逻辑路径 域名/{逻辑路径}/文件相对路径 （默认:/u/f）</param>
    /// <returns></returns>
    public static string GetUploadFileFullPath(string fileRelativePath, string logicalPath = "/u/f")
    {
        var floder = GetUploadDefaultFloder();
        //拼接完整文件路径

        //获取魔法路径
        logicalPath = GetWebUploadLogicalPath(logicalPath);
        var filepath = "";
        if (fileRelativePath.StartsWith(logicalPath))
        {
            filepath = Path.Combine(floder, "upload", $"{fileRelativePath.TrimStart(logicalPath).Replace("/", "\\").TrimStart("\\")}");
        }
        else
        {
            filepath = Path.Combine(floder, $"{fileRelativePath.Replace("/", "\\").TrimStart("\\")}");
        }

        if (!System.IO.File.Exists(filepath)) return null;

        return filepath;
    }

    /// <summary>
    /// 获取文件上传魔法路径
    /// 例如 ：/u/f/abc/20220101/xxx.png
    /// 魔法路径：/u/f
    /// </summary>
    /// <param name="logicalPath">魔法路径，默认为:/u/f</param>
    /// <returns></returns>
    public static string GetWebUploadLogicalPath(string logicalPath = "/u/f")
    {
        var logicalPathTemp = App.Configuration["AppInfo:WebUploadLogicalPath"];
        if (!string.IsNullOrWhiteSpace(logicalPathTemp))
        {
            logicalPath = logicalPathTemp;
        }
        return logicalPath;
    }

    /// <summary>
    /// 获取配置(默认)
    /// </summary>
    /// <typeparam name="TOptions">强类型选项类</typeparam>
    /// <param name="loadPostConfigure"></param>
    /// <returns>TOptions</returns>
    public static TOptions GetConfig<TOptions>(bool loadPostConfigure = false)
    {
        var path = typeof(TOptions).Name;
        if (path.EndsWith("Options"))
        {
            path = path.ReplaceLast("Options", "");
        }
        return App.GetConfig<TOptions>(path);
    }
}